package io.libp2p.example.chat;

import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SocketServer {

    public static final int BUF_SIZE = 16*1024;

    private static final String TAG = "SocketServer";
    private static int PORT = 13131; // 自定义端口
    private ServerSocket serverSocket;
    private static ExecutorService clientThreadPool = Executors.newCachedThreadPool();
    private boolean isRunning = false;

    private String targetIp = "10.0.2.205";
    private int targetPort = 13714;

    PeerConnectHelper helper;

    Map<String,ProxyPort> proxyPortMap = new HashMap<>();

    public SocketServer(String targetIp, int targetPort) {
        this.targetIp = targetIp;
        this.targetPort = targetPort;
    }

    public SocketServer(PeerConnectHelper helper) {
        this.helper = helper;
    }

    // 启动服务端
    public void start() {
        isRunning = true;

        new Thread(() -> {
            try {
                serverSocket = new ServerSocket(PORT++);

                MyLog.D(TAG, String.format("Server started on http://127.0.0.1:%d/", serverSocket.getLocalPort()));

                helper.dataCallback.onConnectChange(helper);

                helper.dataCallback.onDataRecv(helper.peerId, String.format("Server started on http://127.0.0.1:%d/", serverSocket.getLocalPort()));

                while (isRunning) {
                    Socket clientSocket = serverSocket.accept(); // 等待客户端连接

                    MyLog.D(TAG, "Client connected: " + clientSocket.getInetAddress());

                    if(helper.isConnect()) {
                        ProxyPort proxyPort = new ProxyPort(helper, clientSocket, proxyPortCallback);
                        proxyPort.start();
                        proxyPortMap.put(proxyPort.socketId, proxyPort);

                    }else{
                        closeSocket(clientSocket);
                    }
                }

            } catch (IOException e) {
                Log.e(TAG, "Server error: " + e.getMessage());
            }
        }).start();
    }

    public String getAddress(){
        if(serverSocket == null) return "";
        return String.format("http://127.0.0.1:%d/", serverSocket.getLocalPort());
    }

    private void closeSocket(Socket socket){
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void writeDataToPort(DataCmd dataCmd){
        ProxyPort proxyPort = proxyPortMap.get(dataCmd.fileId);
        if(proxyPort != null){
            proxyPort.writeData(dataCmd);
        }
    }

    public void closeClient(DataCmd dataCmd){
        ProxyPort proxyPort = proxyPortMap.remove(dataCmd.fileId);

        if(proxyPort != null){
            MyLog.I(TAG, "Close client from server, socketId="+dataCmd.fileId);
            proxyPort.close();
        }
    }

    // 停止服务端
    public void stop() {
        try {
            isRunning = false;

            if (serverSocket != null) {
                serverSocket.close();
            }

            Log.d(TAG, "Server stopped");
        } catch (IOException e) {
            Log.e(TAG, "Stop error: " + e.getMessage());
        }

        for(Map.Entry<String, ProxyPort> entry: proxyPortMap.entrySet()){
            try {
                entry.getValue().close();
            }catch (Exception e){
                Log.e(TAG, "entry.getValue().close() error: " + e.getMessage());
            }

        }

        proxyPortMap.clear();
    }

    private ProxyPort.ProxyPortCallback proxyPortCallback = sockId -> {
        proxyPortMap.remove(sockId);
    };

    public static Executor getExecutor(){
        return clientThreadPool;
    }
}

